//=========================================================================
// Copyright (C) 2025 The C++ Component Model(COMO) Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//=========================================================================

#include <limits.h>
#include <string.h>
#include <stdio.h>
#include <syslog.h>
#include <stdbool.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#ifdef SYNC_WITH_PHXPAXOS
  #include "PhxPaxosC.h"
#endif
#include "config.h"
#include "voter_message.h"
#include "int64.h"
#include "math_utils.h"
#include "ip_rtnetlink_multi.h"
#include "voter_server.h"
#include "voter_lib.h"

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"

void *g_oPaxosServer = NULL;

extern int ProcessProtocol(VoterMessage *box, int bufSize, int clientId, bool needSync);
extern ChatState *p_Chat;

/**
 * 此时，key         : "Voter#ClientID"
 *      valueSize   : 表决数据
 *      *buf        : 返回值
 *      *bufSize    ：返回空间大小
 */
int MyVoterPhxPaxosSmCallback(int32_t action, const char *key, size_t keySize,
                                          const char *value, size_t valueSize,
                                          char **buf, size_t *bufSize,
                                          int32_t *defaultAction,
                                          void *userData)
{
    (void)action;
    (void)keySize;
    (void)buf;
    (void)userData;

    char voterId[MAX_ID_SIZE + 1];
    uint64_t idTransaction;

    if (NULL == key) {
        return -1;
    }

    if (sscanf(key, MACRO_VOTERID_FMTSTR(MAX_ID_SIZE), voterId, &idTransaction) != 2) {
        return -2;
    }

    // Find the client
    int i;
    for (i = 0;  i <= p_Chat->numclients;  i++) {
        if (strcmp(voterId, p_Chat->clients[i].idVoter) == 0) {
            break;
        }
    }

    VoterClient *c;
    if (i <= p_Chat->numclients) {
        c = &(p_Chat->clients[i]);
    }
    else {
        // 没找到 client，把它添加到节点client列表中

        pthread_mutex_lock(&(p_Chat->mutex));

        for (i = 0;  i < MAX_CLIENTS;  i++) {
            if (-1 == p_Chat->clients[i].fd) {
                break;
            }
        }

        // When num of clients reaches limit, the creation fails
        if (i >= MAX_CLIENTS) {
            syslog(LOG_ERR, "Num of clients reaches limit, fails.");
            return -3;
        }
        else {
            c = &(p_Chat->clients[i]);
            strZcpy(c->idVoter, voterId, MAX_ID_SIZE);
            c->client_port = 0;
            c->client_ip[0] = '\0';

            // Set fd = 0 for PhxPaxos clients
            c->fd = 0;
            p_Chat->numclients++;
        }

        pthread_mutex_unlock(&(p_Chat->mutex));
    }

    // Display received message info
#ifdef __DBEUG__
    printf("Received PhxPaxos data (len=%ld)\n", valueSize);
    printVoterMessage((VoterMessage *)value);
#endif

    // Update the last access time of the port
    clock_gettime(CLOCK_REALTIME, &(c->lastAccessTime));
    int iRet = ProcessProtocol((VoterMessage *)value, valueSize, i, false);
    if (0 != iRet) {
        syslog(LOG_ERR, "MyVoterPhxPaxosSmCallback, ProcessProtocol fails.");
    }

    *bufSize = 0;
    *defaultAction = 0x1111;    // ComoPhxUtils::LevelDbWrite;
    return 0;
}

static bool string_to_int64(const char *str, uint64_t *result)
{
    char *endptr;
    errno = 0;
    *result = strtoull(str, &endptr, 10);

    return (0 == errno) && (endptr != str) && ('\0' ==  *endptr);
}

uint64_t g_idTransaction = 0;

int MyVoterPhxPaxosCheckkeyCallback(const char *pBuf, size_t sizeBuf)
{
    const char *end =  pBuf + sizeBuf;
    const char *p = end;
    char buf[65];

    buf[0] = '\0';
    while (p > pBuf) {
        p++;
        if (*p == '#') {
            p++;

            int i;
            for (i = 0;  (i < 64) && (p < end);  i++) {
                buf[i] = *p++;
            }
            buf[i] = '\0';
        }
    }

    uint64_t num;
    if (! string_to_int64(buf, &num)) {
        return 0;
    }

    if (num > g_idTransaction) {
        return 1;
    }

    return 0;
}


/**
 * 表决结果输出
 *
 * 1. g_voterOutputTokenOrder 在全局变量存储区, outputTokenOrder在栈上，通过两个渠
 *    道传入的序号，得到token，与栈上传入的 token 一致，方可对外输出，防护程序执行路径
 *    错误而引起的错误输出。
 * 2. ...
 */
int MyVoterOutputMsg(char *s, size_t len, int outputTokenOrder, uint64_t token,
                                                             Transaction *trans)
{
    (void)s;
    (void)len;
    (void)trans;

    if ((outputTokenOrder < 0) || (outputTokenOrder >= g_numVoterOutputToken)) {
        return -1;
    }

    if (g_voterOutputTokenOrder != outputTokenOrder) {
        return -2;
    }

    if (g_voterOutputTokenOrder >= g_numVoterOutputToken) {
        return -3;
    }

    if (g_i64bit_Tokens[g_voterOutputTokenOrder] != token) {
        return -4;
    }

    // Output the voting results

    return 0;
}

int MyPhxPaxosMasterChangeCallback(void)
{
#ifdef SYNC_WITH_PHXPAXOS
    int32_t i;

    int iRet = PhxIsIMMaster(&g_oPaxosServer);
    if (0 == iRet) {
        // 本节点是主节点，设对外输出IP地址
        for (i = 0;  i < g_pconfig->numCmdLineSetNetwork;  i++) {
            iprtn_multi(g_pconfig->cmdLineSetNetwork[i]);
        }
    }
    else {
        /**
         * Windows 网络重置 = netsh winsock reset + netsh int ip reset + 驱动重装 + 重启
         */
        // 本节点不是主节点，删对外输出IP地址
        for (i = 0;  i < g_pconfig->numcmdLineDelNetwork;  i++) {
            iprtn_multi(g_pconfig->cmdLineDelNetwork[i]);
        }
    }

#endif
    return 0;
}

#ifdef SYNC_WITH_PHXPAXOS
int PreparePhxPaxos(PhxPaxosSmCallback callback, void *userData)
{
    int iRet = PhxRunPaxos(g_pconfig->sIP_port, g_pconfig->sNodeIP_nodePort,
                                            &g_oPaxosServer, callback, userData);
    if (0 != iRet) {
        syslog(LOG_ERR, "PreparePhxPaxos, PhxRunPaxos fails.");
    }

    printf("PreparePhxPaxos, sIP_port: %s, sNodeIP_nodePort: %s\n",
                              g_pconfig->sIP_port, g_pconfig->sNodeIP_nodePort);
    return 0;
}

#endif

#pragma GCC diagnostic pop
